package com.yungu.swift.assets.driver.service.impl;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.github.miemiedev.mybatis.paginator.domain.PageBounds;
import com.github.miemiedev.mybatis.paginator.domain.PageList;
import com.google.gson.Gson;
import com.yungu.swift.assets.driver.dao.CarMapper;
import com.yungu.swift.assets.driver.dao.DriverMapper;
import com.yungu.swift.assets.driver.dao.DriverQueueMapper;
import com.yungu.swift.assets.driver.dao.DriverSignLogMapper;
import com.yungu.swift.assets.driver.model.dto.*;
import com.yungu.swift.assets.driver.model.param.AdminCrosstownAssignPageParam;
import com.yungu.swift.assets.driver.model.param.AdminDriverMergePageParam;
import com.yungu.swift.assets.driver.model.vo.JoinAssignDriverVo;
import com.yungu.swift.assets.driver.model.vo.QueueCityVO;
import com.yungu.swift.assets.driver.model.vo.QueueDriverVo;
import com.yungu.swift.assets.driver.model.vo.QueueSiteVO;
import com.yungu.swift.assets.driver.service.DriverQueueService;
import com.yungu.swift.autoconfig.lock.RedisLock;
import com.yungu.swift.autoconfig.redis.RedisCacheService;
import com.yungu.swift.base.ResponseData;
import com.yungu.swift.base.model.PageVo;
import com.yungu.swift.constants.CommonConstant;
import com.yungu.swift.constants.DriverConstant;
import com.yungu.swift.constants.MagicConstant;
import com.yungu.swift.constants.OrderConstant;
import com.yungu.swift.lbs.model.Coordinate;
import com.yungu.swift.lbs.model.vo.RegeoVo;
import com.yungu.swift.lbs.service.DistanceService;
import com.yungu.swift.lbs.service.LocationService;
import com.yungu.swift.order.model.dto.OrderDetailDto;
import com.yungu.swift.order.service.OrderComplainService;
import com.yungu.swift.order.service.OrderDetailService;
import com.yungu.swift.order.service.OrderService;
import com.yungu.swift.order.service.OrderTrackMileageService;
import com.yungu.swift.socket.communication.message.UploadLocationMessage;
import com.yungu.swift.socket.communication.service.UploadLocationService;
import com.yungu.swift.system.sys.model.dto.SysPointSiteDto;
import com.yungu.swift.system.sys.model.dto.SysWayDto;
import com.yungu.swift.system.sys.service.SysPointSiteService;
import com.yungu.swift.system.sys.service.SysWayService;
import com.yungu.swift.utils.MapUtils;
import com.yungu.swift.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.CompletableFuture;


/**
 * DriverQueueServiceImpl
 **/
@Slf4j
@Service
public class DriverQueueServiceImpl implements DriverQueueService {

    @Autowired
    private DriverQueueMapper driverQueueMapper;
    @Autowired
    private DriverSignLogMapper driverSignLogMapper;
    @Autowired
    private DriverMapper driverMapper;
    @Autowired
    private CarMapper carMapper;
    @Autowired
    private RedisLock redisLock;
    @Autowired
    private RedisCacheService redisCacheService;

    @Reference
    private LocationService locationService;
    @Reference
    private SysWayService sysWayService;
    @Reference
    private SysPointSiteService sysPointSiteService;
    @Reference
    private OrderTrackMileageService orderTrackMileageService;
    @Reference
    private DistanceService distanceService;
    @Reference
    private OrderService orderService;
    @Reference
    private OrderComplainService orderComplainService;
    @Reference
    private OrderDetailService orderDetailService;
    @Reference
    private UploadLocationService uploadLocationService;

    @Override
    public ResponseData<PageList<DriverQueueDto>> queryPage(Map<String, Object> params, int pageNum, int pageSize) {
        PageBounds pageBounds = new PageBounds(pageNum, pageSize);
        return ResponseData.buildSuccessResponse(driverQueueMapper.queryPage(params, pageBounds));
    }

    @Override
    @Transactional
    public ResponseData<Map<String, Object>> queueUp(String appid, String driUuid, Double driLng, Double driLat, String uploadText,String cityUuid,String siteUuid) {
        //获取司机信息
        DriverDto driver = driverMapper.list(MapUtils.build("uuid", driUuid)).get(0);
        if (null == driver) {
            return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "获取司机信息失败");
        }
        if (driver.getBusinessType() != CommonConstant.BUSINESS_TYPE_POOL) {
            return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "非跨城业务司机");
        }
        if (DriverConstant.DRIVER_STATUS_NORMAL != driver.getStatus()) {
            return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "司机状态异常");
        }
        if(DriverConstant.DRIVER_IS_WORK.equals(driver.getIsWork())){
            int updateResult = driverQueueMapper.updateQueueSite(siteUuid,driUuid);
            if(updateResult!=1){
                return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "更换起点异常，请退出排队后重试");
            }
            return ResponseData.buildSuccessResponse("更换排队起点成功",new HashMap<>(2));
        }
        //这边根据参数给mongodb存点
        if (StringUtils.isNotEmpty(uploadText)) {
            UploadLocationMessage uploadLocationMessage = new Gson().fromJson(uploadText, UploadLocationMessage.class);
            CompletableFuture.runAsync(() -> uploadLocationService.uploadDriverLocation(uploadLocationMessage));
        }
        DriverDto udpDriverDto = new DriverDto();
        udpDriverDto.setIsWork(DriverConstant.DRIVER_IS_WORK);
        udpDriverDto.setUuid(driUuid);
        udpDriverDto.setLatestOnWorkTime(new Date());
        int edit = driverMapper.edit(udpDriverDto);
        if (edit == 0) {
            return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "司机出车失败");
        }
        DriverQueueDto driverQueueDto = new DriverQueueDto();
        driverQueueDto.setUuid(StringUtils.buildUUID());
        driverQueueDto.setDriUuid(driUuid);
        driverQueueDto.setOriginSiteUuid(siteUuid);
        driverQueueDto.setCreateOn(new Date());
        int queueResult = driverQueueMapper.add(driverQueueDto);
        if(queueResult<1){
            return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "出车失败，请重试");
        }

        //插入driver_sign_log表
        DriverSignLogDto driverSignLogDto = new DriverSignLogDto();
        driverSignLogDto.setUuid(StringUtils.buildUUID());
        driverSignLogDto.setCreateTime(new Date());
        driverSignLogDto.setDriverUuid(driUuid);
        driverSignLogDto.setOnWorkTime(new Date());
        driverSignLogDto.setWorkday(new Date());
        driverSignLogDto.setOnWorkLng(driLng);
        driverSignLogDto.setOnWorkLat(driLat);
        int add = driverSignLogMapper.add(driverSignLogDto);
        if (add <= 0) {
            return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "出车失败，请重试");
        }
        //这边将出车司机缓存进出车司机列表
        String onWorkLock = "onWorkLock";
        try {
            if (redisLock.lock(onWorkLock)) {
                Object obj = redisCacheService.get(MagicConstant.PARAMS_ONWORK_DRIVER, false);
                Set<String> onWorkSet = obj == null ? new HashSet<>() : (Set<String>) obj;
                onWorkSet.add(driver.getUuid());
                redisCacheService.set(MagicConstant.PARAMS_ONWORK_DRIVER, onWorkSet, false);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            redisLock.unLock(onWorkLock);
        }
        orderTrackMileageService.statusCycle(driver.getAppid(), driver.getUuid(), null, driver.getCarUuid(), DriverStatusCycleDto.ACTION_ON_WORK);
        return ResponseData.buildSuccessResponse("出车成功", new HashMap<>(2));
    }

    @Override
    public ResponseData<List<DriverQueueDto>> listQueue(String driUuid, String originSiteUuid) {
        Map paras = new HashMap();
        paras.put("uuid", driUuid);
        DriverDto driver = driverMapper.list(paras).get(0);
        if (null == driver) {
            return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "获取司机信息失败");
        }
        paras.clear();
        paras.put("appid", driver.getAppid());
        paras.put("agentUuid", driver.getAgentUuid());
        paras.put("companyUuid", driver.getCompanyUuid());
        paras.put("originSiteUuid", originSiteUuid);
        paras.put("groupByDri", 1);
        List<DriverQueueDto> driverQueueDtos = driverQueueMapper.finByList(paras);
        return ResponseData.buildSuccessResponse("获取排队信息成功", driverQueueDtos);
    }


    @Override
    @Transactional
    public ResponseData<DriverDto> quit(String driUuid, Coordinate coordinate) {
        //获取司机信息
        List<DriverDto> drivers = driverMapper.list(MapUtils.build("uuid", driUuid));
        if (CollectionUtils.isEmpty(drivers)) {
            return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "获取司机信息失败");
        }
        DriverSignLogDto driverSignLogDto = driverSignLogMapper.findOnWork(driUuid);
        if (null == driverSignLogDto) {
            return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "获取不到排队信息");
        }
        DriverDto driver = drivers.get(0);

        //更新上次退出排队时间
        DriverDto driverParam = new DriverDto();
        driverParam.setUuid(driUuid);
        driverParam.setLastOffWorkTime(new Date());
        driverParam.setIsWork(DriverConstant.DRIVER_IS_NOT_WORK);
        int edit = driverMapper.edit(driverParam);
        if (edit <= 0) {
            return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "退出排队失败，请重试");
        }
        //删除排队信息
        driverQueueMapper.batchDelByDri(MapUtils.build("driUuid",driUuid));
        //记录签到日志
        DriverSignLogDto updateSignLogDto = new DriverSignLogDto();
        updateSignLogDto.setUuid(driverSignLogDto.getUuid());
        //下班时间
        updateSignLogDto.setOffWorkTime(new Date());

        Map<String, Object> build = MapUtils.build(5);
        build.put("driverUuid", driUuid);
        build.put("onWorkTime", driverSignLogDto.getOnWorkTime());
        build.put("offWorkTime", new Date());
        //订单总数
        updateSignLogDto.setOrderCount(orderService.getCount(build).getData());
        //投诉总数
        updateSignLogDto.setComplainCnt(orderComplainService.countDriComplain(build).getData());
        updateSignLogDto.setOffWorkLng(coordinate.getLng());
        updateSignLogDto.setOffWorkLat(coordinate.getLat());
        CompletableFuture.runAsync(() -> driverSignLogMapper.edit(updateSignLogDto));
        // 收车司机列表缓存缓存
        String offWorkLock = "offWorkLock";
        try {
            if (redisLock.lock(offWorkLock)) {
                Object onWorkSet = redisCacheService.get(MagicConstant.PARAMS_ONWORK_DRIVER, false);
                if (onWorkSet != null) {
                    ((Set<String>) onWorkSet).remove(driver.getUuid());
                    redisCacheService.set(MagicConstant.PARAMS_ONWORK_DRIVER, onWorkSet, false);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            redisLock.unLock(offWorkLock);
        }
        //记录状态周期
        orderTrackMileageService.statusCycle(driver.getAppid(), driver.getUuid(), null, driver.getCarUuid(),
                DriverStatusCycleDto.ACTION_OFF_WORK);
        return ResponseData.buildSuccessResponse("退出排队操作成功", driver);
    }

    private boolean matchByOrigin(Double driLng, Double driLat, SysPointSiteDto originPointSite) {
        /*获取司机当前位置所处行政区域（【逆】地理位置编码）*/
        String city = null;
        String district = null;
        ResponseData<RegeoVo> responseData = locationService.regeo(driLng, driLat);
        if (responseData.isSuccess() && responseData.getData() != null) {
            RegeoVo regeoVo = responseData.getData();
            RegeoVo.AddressComponent addressComponent = regeoVo.getAddressComponent();
            if (addressComponent != null) {
                city = addressComponent.getCity();
                district = addressComponent.getDistrict();
            }
        }
        //匹配区级（按行政区域匹配：先匹配区县，如果全市则按照市级匹配）
        if (originPointSite.getType() == OrderConstant.POINT_SITE_TYPE_AREA) {
            if ((StringUtils.isNotEmpty(district) && originPointSite.getIncludeAreas().contains(district))
                    || (StringUtils.isNotEmpty(city) && originPointSite.getIncludeAreas().equals("全市") && originPointSite.getCityName().equals(city))) {
                return true;
            }
        } else {
            //匹配固定点（距离中心点500米）
            ResponseData<Double> distanceRS = distanceService.getDistance(driLng, driLat, originPointSite.getCenterLng(),
                    originPointSite.getCenterLat());
            if (distanceRS.isSuccess() && distanceRS.getData() != null) {
                double distance = distanceRS.getData();
                if (distance <= 500d) {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public ResponseData<Map<String, Object>> queueStatus(String driUuid) {
        Map<String, Object> map = new HashMap(4);
        map.put("uuid", driUuid);
        map.put("isWork", DriverConstant.DRIVER_IS_WORK);
        map.put("onQueue", driverMapper.count(map) > 0 ? 1 : 0);
        return ResponseData.buildSuccessResponse(map);
    }

    private boolean matchByWay(Double driLng, Double driLat, String city, String district, SysWayDto way) {
        if (way.getOriginType() == OrderConstant.POINT_SITE_TYPE_AREA) {
            //匹配区级（按行政区域匹配：先匹配区县，如果全市则按照市级匹配）
            if ((StringUtils.isNotEmpty(district) && way.getOriginIncludeAreas().contains(district)) ||
                    (StringUtils.isNotEmpty(city) && way.getOriginIncludeAreas().equals("全市") && way.getOriginCityName().equals(city))) {
                return true;
            }

        } else {//匹配固定点（距离中心点500米）
            ResponseData<Double> distanceRS = distanceService.getDistance(driLng, driLat, way.getOriginLng(), way.getOriginLat());
            if (distanceRS.isSuccess() && distanceRS.getData() != null) {
                double distance = distanceRS.getData();
                if (distance <= 500d) {
                    return true;
                }
            }

        }
        return false;
    }

    private boolean matchByOrigin(Double driLng, Double driLat, String city, String district, SysPointSiteDto originPointSite) {
        //匹配区级（按行政区域匹配：先匹配区县，如果全市则按照市级匹配）
        if (originPointSite.getType() == OrderConstant.POINT_SITE_TYPE_AREA) {
            if ((StringUtils.isNotEmpty(district) && originPointSite.getIncludeAreas().contains(district))
                    || (StringUtils.isNotEmpty(city) && originPointSite.getIncludeAreas().equals("全市") && originPointSite.getCityName().equals(city))) {
                return true;
            }
        } else {
            //匹配固定点（距离中心点500米）
            ResponseData<Double> distanceRS = distanceService.getDistance(driLng, driLat, originPointSite.getCenterLng(), originPointSite.getCenterLat());
            if (distanceRS.isSuccess() && distanceRS.getData() != null) {
                double distance = distanceRS.getData();
                if (distance <= 500d) {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public ResponseData<List<DriverQueueDto>> list(Map<String, Object> params) {
        List<DriverQueueDto> queues = driverQueueMapper.list(params);
        if (null != queues && !queues.isEmpty()) {
            return ResponseData.buildSuccessResponse("获取列表成功", driverQueueMapper.list(params));
        }
        return ResponseData.buildErrorResponse(ResponseData.ERROR_CODE, "查无数据");
    }

    @Override
    public ResponseData<List<QueueDriverVo>> queryQueueDriver(Map<String, Object> params) {
        //取出排队中的司机
        List<QueueDriverVo> queueDriverList = driverQueueMapper.queryQueueDriver(params);
        //当排队中司机身上没有时 该集合用来做订单指派 需要对司机与订单距离进行计算并排序
        if (params.get("noOrder") != null) {
            //排队中
            List<String> driverUuidList = new ArrayList<>();
            for (QueueDriverVo queueDriverVo : queueDriverList) {
                driverUuidList.add(queueDriverVo.getDriverUuid());
            }
            if (driverUuidList.size() == 0) {
                return ResponseData.buildErrorResponse(0, "暂无排队中司机");
            }
            params.put("driverUuidList", driverUuidList);
            //通过司机ID获取司机最新位置点
            ResponseData<Map<String, Object>> pointRsd = locationService.getQueueDriverLocation(params);
            if (pointRsd.isSuccess()) {
                Map<String, Object> driverPointMap = pointRsd.getData();
                Coordinate destCoordinate = (Coordinate) params.get("coordinate");
                for (QueueDriverVo queueDriverVo : queueDriverList) {
                    queueDriverVo.setDistance((Double) driverPointMap.get(queueDriverVo.getDriverUuid()));
                }
                //按照距离正序 空值放最后
                queueDriverList.sort(Comparator.comparing(QueueDriverVo::getDistance, Comparator.nullsLast((v1, v2) -> v1.compareTo(v2))));
            } else {
                return ResponseData.buildErrorResponse(0, "未找到派单半径内的司机");
            }
        }
        return ResponseData.buildSuccessResponse("获取排队中司机成功", queueDriverList);
    }

    @Override
    public ResponseData<PageVo<JoinAssignDriverVo>> queryFreeDriver(AdminCrosstownAssignPageParam freePageParam) {
        PageBounds pageBounds = new PageBounds(freePageParam.getPageNum(), freePageParam.getPageSize());
        PageList<JoinAssignDriverVo> pageList = driverQueueMapper.queryFreeDriver(MapUtils.convertObjToMap(
                freePageParam), pageBounds);
        PageVo<JoinAssignDriverVo> pageVo = new PageVo(freePageParam.getPageNum(), freePageParam.getPageSize(),
                pageList.getPaginator().getTotalCount());
        pageVo.setItems(pageList);
        return ResponseData.buildSuccessResponse(pageVo);
    }

    @Override
    public ResponseData<PageVo<JoinAssignDriverVo>> mergeAssignDriverQuery(AdminDriverMergePageParam mergePageParam) {
        PageBounds pageBounds = new PageBounds(mergePageParam.getPageNum(), mergePageParam.getPageSize());
        PageList<JoinAssignDriverVo> pageList = driverQueueMapper.queryByAssign(MapUtils.convertObjToMap(mergePageParam),
                pageBounds);
        PageVo<JoinAssignDriverVo> pageVo = new PageVo(mergePageParam.getPageNum(), mergePageParam.getPageSize(),
                pageList.getPaginator().getTotalCount());
        pageVo.setItems(pageList);
        return ResponseData.buildSuccessResponse(pageVo);
    }

    @Override
    public ResponseData<PageVo> queryByAssign(AdminCrosstownAssignPageParam param) {
        PageBounds pageBounds = new PageBounds(param.getPageNum(), param.getPageSize());
        Map<String, Object> paraMap = MapUtils.convertObjToMap(param);
        PageList<JoinAssignDriverVo> page = driverQueueMapper.queryByAssign(paraMap, pageBounds);
        OrderDetailDto orderDetailDto = orderDetailService.list(MapUtils.build("orderUuid", param.getOrderUuid())).getData().get(0);
        //非合并派单时 计算司机与订单距离
        if (!param.isMergeAssign()) {
            //排队中
            List<String> driverUuidList = new ArrayList<>();
            for (JoinAssignDriverVo joinAssignDriverVo : page) {
                driverUuidList.add(joinAssignDriverVo.getDriverUuid());
            }
            //通过司机ID获取司机最新位置点
            Map<String, Object> locationParaMap = new HashMap<>();
            Coordinate coordinate = new Coordinate();
            coordinate.setLng(orderDetailDto.getOriginLng());
            coordinate.setLat(orderDetailDto.getOriginLat());
            locationParaMap.put("coordinate", coordinate);
            locationParaMap.put("driverUuidList", driverUuidList);
            ResponseData<Map<String, Object>> pointRsd = locationService.getQueueDriverLocation(locationParaMap);
            if (pointRsd.isSuccess()) {
                Map<String, Object> distanceMap = pointRsd.getData();
                for (JoinAssignDriverVo joinAssignDriverVo : page) {
                    Object distance = distanceMap.get(joinAssignDriverVo.getDriverUuid());
                    if (distance != null) {
                        //计算司机与订单起点之间的直线距离
                        joinAssignDriverVo.setDistance((Double) distance);
                    }
                }
                //按照距离正序 空值放最后
                page.sort(Comparator.comparing(JoinAssignDriverVo::getDistance, Comparator.nullsLast((v1, v2) -> v1.compareTo(v2))));
            }
        }
        PageVo<JoinAssignDriverVo> pageVo = new PageVo(param.getPageNum(), param.getPageSize(),
                page.getPaginator().getTotalCount());
        pageVo.setItems(page);
        return ResponseData.buildSuccessResponse("", pageVo);
    }

    @Override
    public ResponseData<List<QueueCityVO>> queueCity(String driverUuid){
        return ResponseData.buildSuccessResponse(driverQueueMapper.queueCity(driverUuid));
    }

    @Override
    public ResponseData<List<QueueSiteVO>> queueSite(String driverUuid){
        return ResponseData.buildSuccessResponse(driverQueueMapper.queueSite(driverUuid));
    }
}